package org.mbds.wolf.android.idgo800;

import android.content.Context;

import com.gemalto.idgo800.IDGoConstants;
import com.gemalto.idgo800.IDGoErrors;
import com.gemalto.idgo800.IDGoMain;
import com.gemalto.idgo800.apdu.ApduAPI;

/**
 * 
 * @author alesas
 *
 */
public class IDGo800APDUHelper 
{
	private ApduAPI m_apduService = new ApduAPI();
	private int m_apduSessionId = -1;
	private boolean isInitialized = false;
	// Android context
	private Context m_context = null;
	// Application signature provided by Gemalto
	private byte[] m_appSignature = null;
	// Last returned error
	private int lastError;
	// Device type 
	private int deviceType;
	// Reader list
	private int[] readerList = null;
	// Reader Id (according device type parameter)
	private int readerId;
	// Timeout 
	private int timeout = 3000; //3 seconds default timeout
	
	private boolean isOmapiDefault = false;
	private boolean isOmapiUICC = false;
	private boolean isOmapiESE = false;
	private boolean isOmapiSD = false;
	private boolean isBLE = false;
	private boolean isUSB = false;
	private boolean isNFC = false;
	private boolean isSoft = false;
	
	/**
	 * IDGo800APDUHelper
	 * @param context
	 * @param deviceType see IDGoConstants:
	 * - IDGoConstants.DEVICE_TYPE_AUTO_SELECT
	 * - IDGoConstants.DEVICE_TYPE_BLUETOOTH_LE
	 * - IDGoConstants.DEVICE_TYPE_NFC
	 * - IDGoConstants.DEVICE_TYPE_OMAPI_DEFAULT
	 * - IDGoConstants.DEVICE_TYPE_OMAPI_ESE
	 * - IDGoConstants.DEVICE_TYPE_OMAPI_SD
	 * - IDGoConstants.DEVICE_TYPE_OMAPI_UICC
	 * - IDGoConstants.DEVICE_TYPE_SOFT
	 * - IDGoConstants.DEVICE_TYPE_USB
	 * @param appSignature
	 */
	public IDGo800APDUHelper(Context context, int deviceType, byte[] appSignature) throws IDGo800Exception
	{
		this.m_context = context;
		this.m_appSignature = appSignature;
		this.deviceType = deviceType;
		IDGoMain.initialize(context);
		m_apduSessionId = initApduServices(deviceType, m_context.getPackageName(), m_appSignature);
	}

	/**
	 * IDGo800APDUHelper
	 * @param context
	 * @param timeout to wait for response when transmitting APDUs 
	 * @param deviceType see IDGoConstants:
	 * - IDGoConstants.DEVICE_TYPE_AUTO_SELECT
	 * - IDGoConstants.DEVICE_TYPE_BLUETOOTH_LE
	 * - IDGoConstants.DEVICE_TYPE_NFC
	 * - IDGoConstants.DEVICE_TYPE_OMAPI_DEFAULT
	 * - IDGoConstants.DEVICE_TYPE_OMAPI_ESE
	 * - IDGoConstants.DEVICE_TYPE_OMAPI_SD
	 * - IDGoConstants.DEVICE_TYPE_OMAPI_UICC
	 * - IDGoConstants.DEVICE_TYPE_SOFT
	 * - IDGoConstants.DEVICE_TYPE_USB
	 * @param appSignature
	 */
	public IDGo800APDUHelper(Context context, int timeout, int deviceType, byte[] appSignature) throws IDGo800Exception
	{
		this(context, deviceType, appSignature);
		this.timeout = timeout;
	}
	
	/**
	 * IDGo800APDUHelper
	 * @param context
	 * @param appSignature
	 * @throws IDGo800Exception
	 */
	public IDGo800APDUHelper(Context context, byte[] appSignature) throws IDGo800Exception
	{
		this(context, -1, appSignature);
	}

	/**
	 * initApduService
	 * @param deviceType
	 * @param packageName
	 * @param appSignature
	 * @return APDU session ID
	 */
	private int initApduServices(int deviceType, String packageName, byte[] appSignature) throws IDGo800Exception
	{
		if (IDGoMain.isInitialized())
		{
			m_apduSessionId = m_apduService.APDU_Init(packageName, appSignature);
			isInitialized = m_apduSessionId>=0;
			//Get reader
		    if (!isInitialized)
		    {
		    	throwLastError();
		    }
		    else
		    {
		    	readerList = APDU_ListReaders();
		    	checkReaders();
				if (deviceType==-1){
					deviceType =  IDGoConstants.DEVICE_TYPE_AUTO_SELECT;
				}
		    	if ((deviceType!=IDGoConstants.DEVICE_TYPE_AUTO_SELECT) ||
		    		(readerList.length==0) ||
		    		(!isReaderSupported(deviceType)))
		    	{
		    		throw new IDGo800Exception(IDGoErrors.GE_NOT_SUPPORTED);
		    	}
		    }
		}
	    else
	    {
	    	throwLastError();
	    }
	    return m_apduSessionId;
	}

	/**
	 * isInitialized
	 * @return isInitialized
	 */
	public boolean isInitialized()
	{
		return isInitialized;
	}

	/**
	 * getLastError
	 * @return error number int
	 */
	public int getLastError()
	{
		return m_apduService.APDU_GetLastError();
	}
	
	/**
	 * APDU_Connect
	 * @param readerId
	 * @return isConnected
	 */
	public boolean APDU_Connect(int readerId)
	{
		if (m_apduService.APDU_IsConnected(m_apduSessionId))
			return true;
		return m_apduService.APDU_Connect(m_apduSessionId, readerId);
	}
	
	/**
	 * APDU_Disconnect
	 * @return isDisconnected
	 */
	public boolean APDU_Disconnect()
	{
		return m_apduService.APDU_Disconnect(m_apduSessionId);
	}
	
	/**
	 * APDU_GenericCommand
	 * @param cmdId
	 * @param data
	 * @return ADPU response
	 */
	public byte[] APDU_GenericCommand(int cmdId, byte[] data)
	{
		return m_apduService.APDU_GenericCommand(m_apduSessionId, cmdId, data);
	}
	
	/**
	 * APDU_GetATR
	 * @return ATR
	 */
	public byte[] APDU_GetATR()
	{
		return m_apduService.APDU_GetATR(m_apduSessionId);
	}
	
	/**
	 * APDU_IsConnected
	 * @return isConnected
	 */
	public boolean APDU_IsConnected()
	{
		return m_apduService.APDU_IsConnected(m_apduSessionId);
	}
	
	/**
	 * APDU_ListReaders
	 * @return reader device type list
	 */
	public int[] APDU_ListReaders()
	{
		return m_apduService.APDU_ListReaders(m_apduSessionId);
	}
	
	/**
	 * APDU_Transmit
	 * @param apduCommand
	 * @param timeout
	 * @return APDU response
	 * @throws IDGo800Exception
	 */
	public byte[] APDU_Transmit(byte[] apduCommand) throws IDGo800Exception
	{
		if (!APDU_Connect(deviceType))
		{
			throwLastError();
		}
		return m_apduService.APDU_Transmit(m_apduSessionId, apduCommand, timeout);
	}
	
	/**
	 * throwLastError
	 * @throws IDGo800Exception
	 */
	private void throwLastError() throws IDGo800Exception
	{
		int error = m_apduService.APDU_GetLastError();
		if (error==IDGoErrors.GE_OK)
		{
			error = IDGoErrors.GE_UNKNOWN;
		}
		throw new IDGo800Exception(error);
	}

	/**
	 * finalize
	 */
	@Override
	public void finalize() throws Throwable 
	{
		if (IDGoMain.isInitialized())
		{
			try
			{
				// Disconnect APDU service
				m_apduService.APDU_Finalize(m_apduSessionId);
			} catch (Exception e)
			{
				//Check if an exception is thrown
			}
			try
			{
				// Release IDGo800 API
				IDGoMain.release();
			} catch (Exception e)
			{
				//Check if an exception is thrown
			}
		}
		super.finalize();
	}
	
	/**
	 * checkReaders
	 * Sets boolean attributes of supported readers
	 */
	private void checkReaders()
	{
		if (readerList==null)
			return;
		for (int reader: readerList)
		{
			switch (reader)
			{
				case IDGoConstants.DEVICE_TYPE_SOFT:
					isSoft  = true;
					break;
				case IDGoConstants.DEVICE_TYPE_OMAPI_UICC:
					isOmapiUICC = true;
					break;
				case IDGoConstants.DEVICE_TYPE_OMAPI_SD:
					isOmapiSD = true;
					break;
				case IDGoConstants.DEVICE_TYPE_OMAPI_ESE:
					isOmapiESE = true;
					break;
				case IDGoConstants.DEVICE_TYPE_OMAPI_DEFAULT:
					isOmapiDefault = true;
					break;
				case IDGoConstants.DEVICE_TYPE_BLUETOOTH_LE:
					isBLE = true;
					break;
				case IDGoConstants.DEVICE_TYPE_NFC:
					isNFC = true;
					break;
				case IDGoConstants.DEVICE_TYPE_USB:
					isUSB = true;
					break;
			}
			
		}
	}
	
	/**
	 * checkReaderSupported
	 * @param reader deviceType
	 * @return boolean isSupported
	 */
	public boolean isReaderSupported(int deviceType)
	{
			switch (deviceType)
			{
				case IDGoConstants.DEVICE_TYPE_SOFT:
					return isSoft;
				case IDGoConstants.DEVICE_TYPE_OMAPI_UICC:
					return isOmapiUICC;
				case IDGoConstants.DEVICE_TYPE_OMAPI_SD:
					return isOmapiSD;
				case IDGoConstants.DEVICE_TYPE_OMAPI_ESE:
					return isOmapiESE;
				case IDGoConstants.DEVICE_TYPE_OMAPI_DEFAULT:
					return isOmapiDefault;
				case IDGoConstants.DEVICE_TYPE_BLUETOOTH_LE:
					return isBLE;
				case IDGoConstants.DEVICE_TYPE_NFC:
					return isNFC;
				case IDGoConstants.DEVICE_TYPE_USB:
					return isUSB;
				default:
					return false;
			
		}
	}
}
